#include<muduo/base/Logging.h>
#include<muduo/base/Mutex.h>
#include<muduo/net/TcpClient.h>
#include<muduo/net/EventLoopThread.h>
#include<muduo/net/EventLoop.h>
#include "/home/zjx/muduo/examples/protobuf/codec/dispatcher.h"
#include "/home/zjx/muduo/examples/protobuf/codec/codec.h"
#include"request.pb.h"
#include<iostream>

class Client
{
public:
    typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
    typedef std::shared_ptr<zjx::TranslateResponce> TranslateResponcePtr;
    typedef std::shared_ptr<zjx::AddResponce> AddResponcePtr;
private:
    muduo::CountDownLatch _latch;//实现同步
    muduo::net::EventLoopThread _loopthread;//监控
    muduo::net::TcpConnectionPtr _conn;
    muduo::net::TcpClient _client;

    //协议
    ProtobufDispatcher _dispatcher;//协议分发器
    ProtobufCodec _codec;//协议处理器
private:
    void OnTranslate(const muduo::net::TcpConnectionPtr& conn,const TranslateResponcePtr& message , muduo::Timestamp)
    {
        //对于客户端来说，目的就是看到服务器端发回的数据
        std::cout << "翻译结果: " << message->msg() << std::endl;
    }
    void OnAdd(const muduo::net::TcpConnectionPtr& conn, const AddResponcePtr& message , muduo::Timestamp)
    {
        std::cout << "加法结果: " << message->result() << std::endl;
    }

    bool send(const google::protobuf::Message* message)//父类指针指向子类对象
    {
        //连接正常就发送
        if(_conn->connected()) 
        {
            _codec.send(_conn ,*message);
            return true;
        }
        return false;
    }
    void OnConnection(const muduo::net::TcpConnectionPtr& conn)
    {
        if(conn->connected())
        {
            //唤醒+赋值
            _conn = conn;
            _latch.countDown();
            // 这里有一个微妙的时序问题：_latch.countDown() 和 _conn = conn 之间可能有极短的间隙，
            // 主线程在 _latch.wait() 返回后立即使用 _conn，但 _conn 可能还未被设置。所以此处先赋值在唤醒
        }else{
            //连接关闭事件
            _conn.reset();//重置
        }
    }

public:
    void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn ,const MessagePtr& message , muduo::Timestamp)
    {
        //未知消息就调用这个函数进行处理，直接关闭连接
        LOG_INFO <<"onUnknoenMessage" << message->GetTypeName() ;
        conn->shutdown();
    }
    void connect()//想服务器端发起连接请求
    {
        _client.connect();
        _latch.wait();//确保连接成功
    }
    //请求的处理
    void Translate(const std::string& msg)
    {
        zjx::TranslateRequest req;
        req.set_msg(msg);
        send(&req);
    }
    void Add(int num1 , int num2)
    {
        //构建请求
        zjx::AddRequest req;
        req.set_num1(num1);
        req.set_num2(num2);
        //发送请求
        send(&req);//由_codec 进行发送，此处将send进行封装
    }

    Client(const std::string& sip , int sport)
    :_latch(1)
    ,_client(_loopthread.startLoop() , muduo::net::InetAddress(sip , sport) , "Client")
    ,_dispatcher(std::bind(&Client::onUnknownMessage , this
    , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3))
    ,_codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher 
    , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3))
    {
        //向分发器中注册函数,需要指明类型
        _dispatcher.registerMessageCallback<zjx::TranslateResponce>(std::bind(&Client::OnTranslate , this
         , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<zjx::AddResponce>(std::bind(&Client::OnAdd, this 
        , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3));
        //为客户端连接设置对应的回调函数
        _client.setConnectionCallback(std::bind(&Client::OnConnection , this 
         , std::placeholders::_1));
        _client.setMessageCallback(std::bind(&ProtobufCodec::onMessage ,&_codec 
        , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3));//实则是去调用
    }
};

int main()
{
    Client client("127.0.0.1" , 8888);
    client.connect();//向服务器端发起连接请求
    // sleep(1);

    //功能
    client.Translate("hello");
    // client.Translate("语法");
    client.Add(10,20);
    sleep(1);
    return 0;
}
